home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 7684 / 7684.xpi / resources / fmFeeds.js < prev    next >
Text File  |  2009-11-20  |  9KB  |  274 lines

  1. /**
  2.  * Copyright (c) 2008, Jose Enrique Bolanos, Jorge Villalobos
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions are met:
  7.  *
  8.  *  * Redistributions of source code must retain the above copyright notice,
  9.  *    this list of conditions and the following disclaimer.
  10.  *  * Redistributions in binary form must reproduce the above copyright notice,
  11.  *    this list of conditions and the following disclaimer in the documentation
  12.  *    and/or other materials provided with the distribution.
  13.  *  * Neither the name of Jose Enrique Bolanos, Jorge Villalobos nor the names
  14.  *    of its contributors may be used to endorse or promote products derived
  15.  *    from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  20.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
  21.  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  22.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  23.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  24.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  25.  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  26.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  **/
  29.  
  30. var EXPORTED_SYMBOLS = [];
  31.  
  32. const Cc = Components.classes;
  33. const Ci = Components.interfaces;
  34. const Ce = Components.Exception;
  35.  
  36. Components.utils.import("resource://firefm/fmCommon.js");
  37. Components.utils.import("resource://firefm/fmEntities.js");
  38. Components.utils.import("resource://firefm/fmPlayer.js");
  39. Components.utils.import("resource://firefm/fmLogin.js");
  40.  
  41. // Last.FM feed URLs. Note that the array *must* be ordered by type.
  42. const URL_BASE = "http://ws.audioscrobbler.com/1.0";
  43. const URL_USER_BASE = URL_BASE + "/user/$(USER)";
  44. const URL_ARTIST_BASE = URL_BASE + "/artist/$(ARTIST)";
  45.  
  46. const URLS_FEEDS =
  47.   [ URL_USER_BASE + "/friends.xml",
  48.     URL_USER_BASE + "/neighbours.xml",
  49.     URL_USER_BASE + "/topartists.xml",
  50.     URL_ARTIST_BASE + "/similar.xml" ];
  51.  
  52. /**
  53.  * Handles the parsing and temporary storage of feed data, such as top artists,
  54.  * friend lists, etc.
  55.  */
  56. FireFM.Feeds = {
  57.   // Topic notifications sent from this object.
  58.   get TOPIC_FEED_LOADED()  { return "firefm-feed-loaded";  },
  59.   get TOPIC_FEED_CLEARED() { return "firefm-feed-cleared"; },
  60.  
  61.   // Feed types.
  62.   get FEED_FRIENDS() { return 0; },
  63.   get FEED_NEIGHBORS() { return 1; },
  64.   get FEED_TOP_ARTISTS() { return 2; },
  65.   get FEED_SIMILAR_ARTISTS() { return 3; },
  66.  
  67.   /* Logger for this object. */
  68.   _logger : null,
  69.  
  70.   /* User feeds matrix. Holds all data gathered from user feeds. */
  71.   _feeds : new Array(),
  72.  
  73.   /**
  74.    * Returns the amount of feeds we handle.
  75.    * @param the amount of feeds we handle.
  76.    */
  77.   get _feedCount() { return URLS_FEEDS.length; },
  78.  
  79.   /**
  80.    * Obtains the feed of the given type, restricted to the given limit of items.
  81.    * @param aFeedType the type of the feed to get. It can be any of the FEED_
  82.    * constants in this object.
  83.    * @param aLimit the limit of data items in the feed. If the feed has more
  84.    * data items than aLimit, the feed is cropped at the end. It should be -1 for
  85.    * no limit.
  86.    * @return array with the feed data.
  87.    */
  88.   getFeed : function(aFeedType, aLimit) {
  89.     this._logger.debug("getFeed");
  90.  
  91.     if (("number" != typeof(aFeedType)) || (0 > aFeedType) ||
  92.         (this._feedCount <= aFeedType) || ("number" != typeof(aLimit))) {
  93.       this._logger.error(
  94.         "getFeed. Invalid feed type or limit. Type: " + aFeedType +
  95.         ", limit: " + aLimit);
  96.       throw new Ce("Invalid feed type or limit.");
  97.     }
  98.  
  99.     let feed =
  100.       ((0 <= aLimit) ? this._feeds[aFeedType].slice(0, aLimit) :
  101.        this._feeds[aFeedType]);
  102.  
  103.     return feed;
  104.   },
  105.  
  106.   /**
  107.    * Obtains the size feed of the given type.
  108.    * @param aFeedType the type of the feed to get. It can be any of the FEED_
  109.    * constants in this object.
  110.    * @return the amount of items on the specified feed.
  111.    */
  112.   getFeedSize : function(aFeedType) {
  113.     this._logger.debug("getFeedSize");
  114.  
  115.     if (("number" != typeof(aFeedType)) || (0 > aFeedType) ||
  116.         (this._feedCount <= aFeedType)) {
  117.       this._logger.error("getFeedSize. Invalid feed type. Type: " + aFeedType);
  118.       throw new Ce("Invalid feed type.");
  119.     }
  120.  
  121.     return this._feeds[aFeedType].length;
  122.   },
  123.  
  124.   /**
  125.    * Sends a request for the specified feed type.
  126.    * @param aFeedType the type of the feed to fetch.
  127.    */
  128.   fetchFeed : function(aFeedType) {
  129.     this._logger.debug("fetchFeed");
  130.  
  131.     if (("number" != typeof(aFeedType)) || (0 > aFeedType) ||
  132.         (this._feedCount <= aFeedType)) {
  133.       this._logger.error("fetchFeed. Invalid feed type: " + aFeedType);
  134.       throw new Ce("Invalid feed type.");
  135.     }
  136.  
  137.     let userName = encodeURIComponent(FireFM.Login.userName);
  138.     let artist = "";
  139.     if (null != FireFM.Player.track) {
  140.       artist = encodeURIComponent(FireFM.Player.track.artist);
  141.     }
  142.  
  143.     let url = URLS_FEEDS[aFeedType];
  144.     let that = this;
  145.  
  146.     // check that the feed exists.
  147.     if (null != url) {
  148.       url = url.replace(/\$\(USER\)/, userName);
  149.       url = url.replace(/\$\(ARTIST\)/, artist);
  150.       // delete the data.
  151.       FireFM.Remote.fetchFeed(
  152.         url, function(aResponse) { that._parseFeed(aFeedType, aResponse); });
  153.     }
  154.   },
  155.  
  156.   /**
  157.    * Clears the feed of the specified types.
  158.    * @param aFeedType the type of the feed to clear.
  159.    */
  160.   _clearFeed : function(aFeedType) {
  161.     this._logger.trace("_clearFeed");
  162.  
  163.     // check that the feed is valid.
  164.     if (null != URLS_FEEDS[aFeedType]) {
  165.       let feedData = this._feeds[aFeedType];
  166.       // delete the data.
  167.       feedData.splice(0, feedData.length);
  168.  
  169.       FireFM.obsService.notifyObservers(
  170.         null, this.TOPIC_FEED_CLEARED, aFeedType);
  171.     }
  172.   },
  173.  
  174.   /**
  175.    * Parses the feed data received from the Last.fm server.
  176.    * @param aFeedType the type of feed being parsed.
  177.    * @param aResponse the XMLHTTPRequest object that holds the response data.
  178.    */
  179.   _parseFeed : function(aFeedType, aResponse) {
  180.     this._logger.trace("_parseFeed. Type: " + aFeedType);
  181.  
  182.     if (null != aResponse) {
  183.       let feed = this._feeds[aFeedType];
  184.       let doc = aResponse.responseXML;
  185.       let listItems = doc.documentElement.childNodes;
  186.       let itemCount = listItems.length;
  187.       let isArtistFeed =
  188.         (this.FEED_TOP_ARTISTS == aFeedType ||
  189.          this.FEED_SIMILAR_ARTISTS == aFeedType);
  190.       let listItem, name, url, imagePath;
  191.  
  192.       this._clearFeed(aFeedType);
  193.  
  194.       for (let i = 0; i < itemCount; i++) {
  195.         listItem = listItems[i];
  196.  
  197.         if (Ci.nsIDOMNode.ELEMENT_NODE == listItem.nodeType) {
  198.           if (isArtistFeed) {
  199.             name = listItem.getElementsByTagName("name")[0].textContent;
  200.           } else {
  201.             name = listItem.getAttribute("username");
  202.           }
  203.  
  204.           url = listItem.getElementsByTagName("url")[0].textContent;
  205.           imagePath = listItem.getElementsByTagName("image")[0].textContent;
  206.           feed.push(new FireFM.Artist(name, url, imagePath));
  207.         }
  208.       }
  209.  
  210.       FireFM.obsService.notifyObservers(
  211.         null, this.TOPIC_FEED_LOADED, aFeedType);
  212.     } else {
  213.       this._logger.error("_parseFeed. No response.");
  214.     }
  215.   },
  216.  
  217.   /**
  218.    * Observes notifications of authentication.
  219.    * @param aSubject The object that experienced the change.
  220.    * @param aTopic The topic being observed.
  221.    * @param aData The data related to the change.
  222.    */
  223.   observe : function(aSubject, aTopic, aData) {
  224.     this._logger.debug("observe");
  225.  
  226.     switch (aTopic) {
  227.  
  228.       case FireFM.Player.TOPIC_TRACK_LOADED:
  229.         this._clearFeed(this.FEED_SIMILAR_ARTISTS);
  230.         this.fetchFeed(this.FEED_SIMILAR_ARTISTS);
  231.         break;
  232.  
  233.       case FireFM.Player.TOPIC_PLAYER_STOPPED:
  234.         this._clearFeed(this.FEED_SIMILAR_ARTISTS);
  235.         break;
  236.  
  237.       case FireFM.Login.TOPIC_USER_AUTHENTICATION:
  238.         if (null != aData) {
  239.           this.fetchFeed(this.FEED_FRIENDS);
  240.           this.fetchFeed(this.FEED_NEIGHBORS);
  241.           this.fetchFeed(this.FEED_TOP_ARTISTS);
  242.         } else {
  243.           this._clearFeed(this.FEED_FRIENDS);
  244.           this._clearFeed(this.FEED_NEIGHBORS);
  245.           this._clearFeed(this.FEED_TOP_ARTISTS);
  246.         }
  247.  
  248.         break;
  249.     }
  250.   }
  251. };
  252.  
  253. /**
  254.  * FireFM.Feeds constructor.
  255.  */
  256. (function() {
  257.   let feedCount = URLS_FEEDS.length;
  258.  
  259.   this._logger = FireFM.getLogger("FireFM.Feeds");
  260.   this._logger.debug("init");
  261.  
  262.   // initialize the feed array to empty data.
  263.   for (let type = 0; type < this._feedCount; type++) {
  264.     this._feeds[type] = new Array();
  265.   }
  266.  
  267.   FireFM.obsService.addObserver(
  268.     this, FireFM.Login.TOPIC_USER_AUTHENTICATION, false);
  269.   FireFM.obsService.addObserver(
  270.     this, FireFM.Player.TOPIC_TRACK_LOADED, false);
  271.   FireFM.obsService.addObserver(
  272.     this, FireFM.Player.TOPIC_PLAYER_STOPPED, false);
  273. }).apply(FireFM.Feeds);
  274.